package bootstrap.liftweb

import _root_.net.liftweb.util._
import _root_.net.liftweb.common._
import _root_.net.liftweb.http._
import _root_.net.liftweb.http.provider._
import _root_.net.liftweb.sitemap._
import _root_.net.liftweb.sitemap.Loc._
import Helpers._
import _root_.net.liftweb.mapper.{DB, ConnectionManager, Schemifier, DefaultConnectionIdentifier, ConnectionIdentifier}
import _root_.java.sql.{Connection, DriverManager}
import _root_.com.ding.model._
import _root_.javax.servlet.http.{HttpServletRequest}

/**
 * A class that's instantiated early and run.  It allows the application
 * to modify lift's environment
 */
class Boot {
    def boot {
        if (!DB.jndiJdbcConnAvailable_?)
            DB.defineConnectionManager(DefaultConnectionIdentifier, DBVendor)

        // where to search snippet
        LiftRules.addToPackages("com.ding")
        Schemifier.schemify(true, Log.infoF _, User, LiftDocument)

        LiftRules.dispatch.append({
            case Req(List("doc","add"), _, _) => () => addDocContent()
            })

        // Build SiteMap
        val entries = Menu(Loc("Home", List("index"), "Home")) :: User.sitemap
        LiftRules.setSiteMap(SiteMap(entries:_*))

        /*
         * Show the spinny image when an Ajax call starts
         */
        LiftRules.ajaxStart =
            Full(() => LiftRules.jsArtifacts.show("ajax-loader").cmd)

        /*
         * Make the spinny image go away when it ends
         */
        LiftRules.ajaxEnd =
            Full(() => LiftRules.jsArtifacts.hide("ajax-loader").cmd)

        LiftRules.early.append(makeUtf8)

        LiftRules.loggedInTest = Full(() => User.loggedIn_?)

        S.addAround(DB.buildLoanWrapper)
    }

    /**
     * Force the request to be UTF-8
     */
    /**
     * Force the request to be UTF-8
     */
    private def makeUtf8(req: HTTPRequest) {
        req.setCharacterEncoding("UTF-8")
    }

    private def addDocContent() : Box[LiftResponse] = {
        val reqbox = S.request
        val req = reqbox.open_!
        val reqbody : Array[Byte] = req.body.openOr(Array())
        val responsemsg = if (reqbody.isEmpty){
            "reqbody empty"
        }
        else{
            val doc_entry = LiftDocument.create.content(reqbody)
            doc_entry.save
            doc_entry.doc_id.toString
        }
        Full(InMemoryResponse((responsemsg.getBytes("UTF-8")), "Content-Type" -> "text/plain; charset=utf-8" :: Nil, Nil, 200))
    }

}

/**
 * Database connection calculation
 */
object DBVendor extends ConnectionManager {
    private var pool: List[Connection] = Nil
    private var poolSize = 0
    private val maxPoolSize = 4

    private def createOne: Box[Connection] = try {
        val driverName: String = Props.get("db.driver") openOr
        "org.apache.derby.jdbc.EmbeddedDriver"

        val dbUrl: String = Props.get("db.url") openOr
        "jdbc:derby:lift_example;create=true"

        Class.forName(driverName)

        val dm = (Props.get("db.user"), Props.get("db.password")) match {
            case (Full(user), Full(pwd)) =>
                DriverManager.getConnection(dbUrl, user, pwd)

            case _ => DriverManager.getConnection(dbUrl)
        }

        Full(dm)
    } catch {
        case e: Exception => e.printStackTrace; Empty
    }

    def newConnection(name: ConnectionIdentifier): Box[Connection] =
        synchronized {
            pool match {
                case Nil if poolSize < maxPoolSize =>
                    val ret = createOne
                    poolSize = poolSize + 1
                    ret.foreach(c => pool = c :: pool)
                    ret

                case Nil => wait(1000L); newConnection(name)
                case x :: xs => try {
                        x.setAutoCommit(false)
                        Full(x)
                    } catch {
                        case e => try {
                                pool = xs
                                poolSize = poolSize - 1
                                x.close
                                newConnection(name)
                            } catch {
                                case e => newConnection(name)
                            }
                    }
            }
        }

    def releaseConnection(conn: Connection): Unit = synchronized {
        pool = conn :: pool
        notify
    }
}


